package info.ms.neural;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import info.ms.neural.degrade.IBizDegrade;
import info.ms.neural.degrade.runner.IDegradeRunner;
import info.ms.neural.engine.NeuralEngine;
import info.ms.neural.engine.entity.NeuralConf;
import info.ms.neural.flowrate.runner.IFlowrateRunner;
import info.ms.neural.moduler.Moduler;
import info.ms.neural.passrate.IPassRate;
import info.ms.neural.passrate.core.PassRateFactory;

/**
 * 中枢神经<br>
 * 1.放通率控制<br>√
 * 2.流量控制<br>√
 * 3.服务降级<br>√
 * 4.幂等机制<br>TODO
 * 5.超时控制<br>√
 * 6.容错转移<br>√
 * 7.线程池舱壁<br>√
 * 8.线程隔离<br>√
 * 9.慢性重试<br>√
 * 10.MOCK服务<br>√
 * @author lry
 *
 * @param <REQ>
 * @param <RES>
 */
public class CentralNeural<REQ, RES> {
	
	public static final Logger logger=LogManager.getLogger(CentralNeural.class);

	private Moduler<REQ, RES> moduler;
	/**
	 * 核心引擎
	 */
	private NeuralEngine<REQ, RES> neuralEngine=new NeuralEngine<REQ, RES>();
	
	public CentralNeural(Moduler<REQ, RES> moduler) {
		this.moduler=moduler;
	}
	
	public RES neural(String id, final NeuralConf conf, final IRoute<REQ, RES> route, final IBizDegrade<REQ, RES> bizDegrade, REQ req) {
		try {
			//$NON-NLS-第一步:放通率校验$
			IPassRate passRate=new PassRateFactory(conf);
			passRate.passRate();//拒绝放通,则直接向外抛异常
			
			//$NON-NLS-第二步:流量控制$
			RES flowrateRES=moduler.getFlowrate().flowrate(id, req, new IFlowrateRunner<REQ, RES>(){
				public RES runner(REQ req) throws Throwable {
					RES res=null;
					
					if(logger.isInfoEnabled()){
						logger.info("The Degrade msg is:{DegradeEnable["+conf.isDegradeEnable()+"], DegradeType["+conf.getDegradeType()+"], StrategyType["+conf.getStrategyType()+"]}.");
					}
					
					//$NON-NLS-第三步:服务降级$
					res=moduler.getDegrade().degrade(conf, new IDegradeRunner<REQ, RES>() {
						public RES engine(NeuralConf conf, IDegradeRunner<REQ, RES> runner, REQ req) throws Throwable {
							//$NON-NLS-第四步:幂等SLA$
							
							//$NON-NLS-第五步:容错引擎$
							return neuralEngine.engine(conf, route, req);
						}
						public RES mock(String neuralId, REQ req) throws Throwable{
							return route.mock(neuralId, req);
						}
					}, req, bizDegrade);
					
					return res;
				}
			});
			return flowrateRES;
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
}
